package rnxfilessearch

import (
	"dgo/goutils/gis"
	"fmt"
	"gitee.com/ymofen/gobase"
	"gitee.com/ymofen/golog"
	"gnssa/pkg/utils"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

type fileInfo struct {
	fullName string
	t0       time.Time
	staid    string
	xyz      [3]float64
	timelist []string
}

type RnxGzFilesReader struct {
	cachefile string
}

func NewRnxGzFilesSearch() *RnxGzFilesReader {
	return &RnxGzFilesReader{}
}

func (this *RnxGzFilesReader) readRnxFile(info *fileInfo, fileName string) (err error) {
	//   4231162.7790  -332746.9230  4745130.6810                  APPROX POSITION XYZ
	_, err = gobase.ReadFileLine(fileName, func(idx int64, line []byte) bool {
		str := gobase.Trim(string(line))
		if strings.HasSuffix(str, "APPROX POSITION XYZ") {
			xyzstr := gobase.ReplaceMultiSpace2One(str, " ")
			info.xyz[0], info.xyz[1], info.xyz[2] = gobase.StrTo3Float64(xyzstr, " ")
			return false
		}
		return true
	})
	return err
}

func (this *RnxGzFilesReader) processGzFile(info *fileInfo, fullName string) (err error) {
	var newFile string

	newFile, err = utils.DecompressZipToCurrentDir(fullName)
	if err != nil {
		golog.Warnf("DecompressZipToCurrentDir %s err:%s", fullName, err.Error())
		return err
	}

	defer gobase.DeleteFileOrDir(newFile)

	validFlag := this.extractFromFileName(info, newFile)

	if !validFlag {
		return fmt.Errorf("invalid filename %s", fullName)
	}

	return this.readRnxFile(info, newFile)
}

func (this *RnxGzFilesReader) extractFromFileName(info *fileInfo, fullName string) (validFlag bool) {
	fname := gobase.ExtractFileName(fullName)
	ext := filepath.Ext(fullName)

	{
		// GANP00SVK_R_20230110000_01D_MN.rnx
		// BRST00FRA_R_20230110000_01D_30S_MO.23o
		// WTZZ00DEU_R_20242020000_01D_30S_MO.crx
		nameTokens := strings.Split(gobase.DeleteFileNameExt(fname), "_")
		if len(nameTokens) == 6 || len(nameTokens) == 5 {
			info.staid = nameTokens[0]
			syear := nameTokens[2][:4]
			sdoy := nameTokens[2][4:7]
			t0 := time.Date(gobase.StrToIntDef(syear, 0), 1, 1, 0, 0, 0, 0, time.UTC)
			t0 = t0.Add(time.Duration(gobase.StrToIntDef(sdoy, 0)-1) * time.Hour * 24)
			info.t0 = t0
			validFlag = true
			return
		}
	}

	// MORP2020.24D, gdzj2790.24o
	{
		if len(fname) == 12 {
			fname = gobase.DeleteFileNameExt(fname)
			staId := fname[:len(fname)-4]
			syear := "20" + ext[1:3]
			sdoy := fname[len(fname)-4 : len(fname)-1]
			t0 := time.Date(gobase.StrToIntDef(syear, 0), 1, 1, 0, 0, 0, 0, time.UTC)
			t0 = t0.Add(time.Duration(gobase.StrToIntDef(sdoy, 0)-1) * time.Hour * 24)
			validFlag = true
			info.staid = staId
			info.t0 = t0
			return
		}

	}

	return false
}

// BRST00FRA_R_20230110000_01D_30S_MO.23o
func (this *RnxGzFilesReader) processRnxObsFile(info *fileInfo, fullName string) (err error) {

	validFlag := this.extractFromFileName(info, fullName)

	if !validFlag {
		return fmt.Errorf("invalid filename %s", fullName)
	}

	return this.readRnxFile(info, fullName)
}

func (this *RnxGzFilesReader) writeStaFile(dir string, fileList []fileInfo) (err error) {
	var fileMap = make(map[string]*fileInfo)
	for i := 0; i < len(fileList); i++ {
		itm := fileMap[fileList[i].staid]
		if itm == nil {
			itm = &fileList[i]
			fileMap[fileList[i].staid] = itm
			itm.timelist = append(itm.timelist, fmt.Sprintf("%.4d%.3d", fileList[i].t0.Year(), fileList[i].t0.Day()))
		} else {
			itm.timelist = append(itm.timelist, fmt.Sprintf("%.4d%.3d", fileList[i].t0.Year(), fileList[i].t0.Day()))
		}
	}

	lst := make([]fileInfo, 0, len(fileList))
	for _, v := range fileMap {
		sort.Strings(v.timelist)
		lst = append(lst, *v)
	}
	sort.Slice(lst, func(i, j int) bool {
		return lst[i].staid < lst[j].staid
	})

	var sb gobase.BytesBuilder
	for _, v := range lst {
		b, l, h := gis.EcefXyz2PosiLatLngH(v.xyz[0], v.xyz[1], v.xyz[2])
		sb.Appendf("%s\t%.4f,%.4f,%.4f\t%.4f,%.4f,%.4f\n", v.staid, v.xyz[0], v.xyz[1], v.xyz[2], b, l, h)
	}

	err = gobase.RewriteFile(dir+gobase.PathSeparator+".sta", sb.Bytes())
	return err
}

func (this *RnxGzFilesReader) writeTaskList(dir string, fileList []fileInfo) (err error) {

	sort.Slice(fileList, func(i, j int) bool {
		if fileList[i].staid == fileList[j].staid {
			return fileList[i].t0.Before(fileList[j].t0)
		}
		return fileList[i].staid < fileList[j].staid
	})

	var sb gobase.BytesBuilder
	for _, v := range fileList {
		sb.Appendf("%s\t%.4f,%.4f,%.4f\t%.4d\t%.3d\t%s\n", v.staid, v.xyz[0], v.xyz[1], v.xyz[2], v.t0.Year(), v.t0.YearDay(), v.fullName)
	}

	err = gobase.RewriteFile(dir+gobase.PathSeparator+".list", sb.Bytes())
	return err
}

func (this *RnxGzFilesReader) Search(dir string, outputDir string) {
	var fileList []fileInfo

	t0 := time.Now()
	gobase.RangeFiles(dir, 1, func(path string, file os.FileInfo) bool {
		fileName := file.Name()
		fileNameUpper := strings.ToUpper(fileName)
		ext := strings.ToUpper(filepath.Ext(fileName))
		if strings.HasSuffix(fileName, "_MN.rnx.gz") { // 不处理
			return true
		}

		if ext == ".GZ" || strings.HasSuffix(fileNameUpper, ".Z") {
			info := fileInfo{fullName: dir + gobase.PathSeparator + fileName}
			err := this.processGzFile(&info, info.fullName)
			if err != nil {
				golog.Errorf("%s err %s", fileName, err.Error())
			} else {
				fileList = append(fileList, info)
			}
		} else if ext == ".CRX" || strings.HasSuffix(fileNameUpper, "D") || strings.HasSuffix(fileNameUpper, "O") {
			info := fileInfo{fullName: dir + gobase.PathSeparator + fileName}
			err := this.processRnxObsFile(&info, info.fullName)
			if err != nil {
				golog.Errorf("%s err %s", fileName, err.Error())
			} else {
				fileList = append(fileList, info)
			}
		}
		return true
	}, func(dirname string, err error) bool {
		golog.Errorf("%s, err:%s", dirname, err)
		return true
	})
	if len(outputDir) == 0 {
		outputDir = dir
	}

	err := this.writeStaFile(outputDir, fileList)
	if err != nil {
		gobase.Warnf("sta, %s", err.Error())
	}

	err = this.writeTaskList(outputDir, fileList)
	if err != nil {
		gobase.Warnf("tasklist, %s", err.Error())
	}

	gobase.Infof("%d, consume:%d(ms)", len(fileList), time.Since(t0).Milliseconds())
	// fmt.Printf("%s", sb.String())
}
